﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WebUtils;

namespace OAuth
{
    class DefaultSession : IOAuthSession
    {
        public DefaultSession(IOAuthSessionParameter pOAuthSessionParameter)
        {
            OAuthSessionParameter = pOAuthSessionParameter;
        }

        public IOAuthSessionParameter OAuthSessionParameter { get; private set; }

        public void RefreshSession()
        {
            OAuthSessionParameter.TokenStorage.TokenSecret =
                OAuthSessionParameter.TokenStorage.TokenValue = null;
            StartSession();
        }

        public void StartSession()
        {
            // we only need to do auth if we don't have any access keys present
            if (!(string.IsNullOrEmpty(OAuthSessionParameter.TokenStorage.TokenSecret) &&
                 string.IsNullOrEmpty(OAuthSessionParameter.TokenStorage.TokenValue)))
                return;

            // auth is a three-step process
            // get auth tokens
            // user authorizes application
            // get access tokens

            string requestToken = null, requestTokenSecret = null;

            // filter out the token and secret
            // keep the result
            // we are going to make this a list
            // to force early evaluation of the enumerable
            var remainingTokenRequestResponseOAuthSessionParameters = new List<UnEncodedURLParameter>(
                Utilities.ParseParameters(OAuthSessionParameter.CallWeb(CreateRequestTokenWebRequest().CreateSignedWebRequest()))
                    .Where(param =>
                        {
                            if (param.Key == "oauth_token") requestToken = param.Value;
                            else if (param.Key == "oauth_token_secret") requestTokenSecret = param.Value;
                            else return true;
                            return false;
                        }));

            // swap out the set token storage with a new one
            // that will hold the requestToken and secret
            var oldStorage = OAuthSessionParameter.TokenStorage;
            OAuthSessionParameter.TokenStorage = new TransitoryTokenStorage() 
            {
                TokenValue = requestToken,
                TokenSecret = requestTokenSecret
            };

            // do the user redirection thing
            OAuthSessionParameter.ServiceProvider.WithRequestTokenResult(requestToken, requestTokenSecret, remainingTokenRequestResponseOAuthSessionParameters);

            // do the access token request
            // early eval of the enumerable again via a list instance
            string accessToken = null, accessTokenSecret = null;
            var remainingAccessRequestResponseOAuthSessionParameters = new List<UnEncodedURLParameter>(
                Utilities.ParseParameters(OAuthSessionParameter.CallWeb(CreateAccessTokenRequest(requestToken, requestTokenSecret).CreateSignedWebRequest()))
                    .Where(param =>
                    {
                        if (param.Key == "oauth_token") accessToken = param.Value;
                        else if (param.Key == "oauth_token_secret") accessTokenSecret = param.Value;
                        else return true;
                        return false;
                    }));

            // now we must swap back the token storage
            OAuthSessionParameter.TokenStorage = oldStorage;

            // store the access token and secret
            OAuthSessionParameter.TokenStorage.TokenValue = accessToken;
            OAuthSessionParameter.TokenStorage.TokenSecret = accessTokenSecret;

            // allow the client to intercept the result of the access token request
            OAuthSessionParameter.ServiceProvider.WithAccessTokenResult(accessToken, accessTokenSecret, remainingAccessRequestResponseOAuthSessionParameters);
        }

        private IUnsignedWebRequest CreateAccessTokenRequest(string pToken, string pTokenSecret)
        {
            return new DefaultUnsignedWebRequest(
                OAuthSessionParameter,
                OAuthSessionParameter.ServiceProvider.AccessTokenRequest.Verb,
                OAuthSessionParameter.ServiceProvider.AccessTokenRequest.Address,
                OAuthSessionParameter.ConsumerKey,
                pToken,
                pTokenSecret,
                OAuthSessionParameter.ServiceProvider.AccessTokenSignatureMethod,
                OAuthSessionParameter.ServiceProvider.AccessAuthInfoLocation,
                OAuthSessionParameter.ServiceProvider.AccessTokenRequest.GetParameters,
                OAuthSessionParameter.ServiceProvider.AccessTokenRequest.PostParameters,
                OAuthSessionParameter.ServiceProvider.AccessTokenRequest.Headers);
        }

        private IUnsignedWebRequest CreateRequestTokenWebRequest()
        {
            return new DefaultUnsignedWebRequest(
                OAuthSessionParameter,
                OAuthSessionParameter.ServiceProvider.RequestTokenRequest.Verb,
                OAuthSessionParameter.ServiceProvider.RequestTokenRequest.Address,
                OAuthSessionParameter.ConsumerKey,
                null,                                                       // null token for the token request
                null,                                                       // null token secret
                OAuthSessionParameter.ServiceProvider.RequestTokenSignatureMethod,
                OAuthSessionParameter.ServiceProvider.RequestAuthInfoLocation,
                OAuthSessionParameter.ServiceProvider.RequestTokenRequest.GetParameters,
                OAuthSessionParameter.ServiceProvider.RequestTokenRequest.PostParameters,
                OAuthSessionParameter.ServiceProvider.RequestTokenRequest.Headers);
        }

        public ISignedWebRequest SignWebRequest(IWebRequest pRequest)
        {
            return new DefaultUnsignedWebRequest(
                OAuthSessionParameter,
                pRequest.Verb,
                pRequest.Address,
                OAuthSessionParameter.ConsumerKey,
                OAuthSessionParameter.TokenStorage.TokenValue,
                OAuthSessionParameter.TokenStorage.TokenSecret,
                OAuthSessionParameter.ServiceProvider.ProtectedResourceSignatureMethod,
                OAuthSessionParameter.ServiceProvider.ProtectedResourceAuthInfoLocation,
                pRequest.GetParameters,
                pRequest.PostParameters,
                pRequest.Headers).CreateSignedWebRequest();
        }
    }
}
